/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.skatiger.jrove.util;

import java.util.Iterator;
import java.util.ServiceLoader;
import java.util.WeakHashMap;

public abstract class ServiceLoaderUtil {
    private static final WeakHashMap<Class<?>, ServiceLoader<?>> cache;
    static {
        cache = new WeakHashMap<Class<?>, ServiceLoader<?>>();
    }

    private static final ServiceLoader<?> findInCache(Class<?> clazz) {
        return cache.get(clazz);
    }

    @SuppressWarnings("unchecked")
    private static final <E> ServiceLoader<E> getServiceLoader(Class<E> clazz) {
        ServiceLoader<?> sl = findInCache(clazz);
        if (sl == null) {
            sl = ServiceLoader.load(clazz);
            putToCache(clazz, sl);
        }
        return (ServiceLoader<E>) sl;
    }

    @SuppressWarnings("unchecked")
    private static final <E> ServiceLoader<E> getServiceLoader(Class<E> clazz,
            ClassLoader classLoader) {
        ServiceLoader<?> sl = findInCache(clazz);
        if (sl == null) {
            sl = ServiceLoader.load(clazz, classLoader);
            putToCache(clazz, sl);
        }
        return (ServiceLoader<E>) sl;
    }

    /**
     * find service in current ClassLoader use ServiceLoader
     * 
     * @param <E>
     * @param clazz
     * @return service instance
     */
    public static final <E> E loadService(Class<E> clazz) {
        Iterator<E> itE = loadServices(clazz);
        if (itE.hasNext()) {
            return itE.next();
        }
        return null;
    }

    /**
     * find service in ClassLoader use ServiceLoader
     * 
     * @param <E>
     *            Service Class Generic type
     * @param clazz
     *            class
     * @param classLoader
     *            class in classloader
     * @return
     */
    public static final <E> E loadService(Class<E> clazz,
            ClassLoader classLoader) {
        Iterator<E> itE = loadServices(clazz, classLoader);
        if (itE.hasNext()) {
            return itE.next();
        }
        return null;
    }

    /**
     * find service in current ClassLoader use ServiceLoader
     * 
     * @param <E>
     * @param clazz
     * @return service instance iterator
     */
    public static final <E> Iterator<E> loadServices(Class<E> clazz) {
        ServiceLoader<E> loader = getServiceLoader(clazz);
        Iterator<E> itE = loader.iterator();
        return itE;
    }

    /**
     * find services in ClassLoader use ServiceLoader
     * 
     * @param <E>
     * @param clazz
     * @return service instance iterator
     */
    public static final <E> Iterator<E> loadServices(Class<E> clazz,
            ClassLoader classLoader) {
        ServiceLoader<E> loader = getServiceLoader(clazz, classLoader);
        Iterator<E> itE = loader.iterator();
        return itE;
    }

    private static final void putToCache(Class<?> clazz, ServiceLoader<?> sl) {
        cache.put(clazz, sl);
    }
}
